package project.master;

import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicReference;

import common.db.JdbcSource;
import common.db.jdbc.Jdbc;
import common.master.IntervalCache;

import core.exception.PhysicalException;
import core.exception.ThrowableUtil;
import core.util.MapUtil;

/**
 * コードマスタ取得保持クラス
 *
 * @author Tadashi Nakayama
 * @version 1.0.0
 */
public final class CodeKeeperImpl extends IntervalCache implements CodeKeeper {

	/** コードマスタ取得クエリ */
	private static final String SEL_MS_CODE =
			"SELECT NM_SB_CODE, CODE, NM_CODE, NM_CODE_RYAKU, "
			+ "NM_CODE_OPTION, NO_ORDER, TX_COMMENT "
			+ "FROM MS_CODE "
			+ "WHERE SB_CODE = ? "
			+ "ORDER BY NO_ORDER, CODE";

	/** 自身保持用 */
	private static final AtomicReference<CodeKeeperImpl> INSTANCE = new AtomicReference<>();

	/** 格納用ハッシュ */
	private final ConcurrentMap<String, List<Code>> code =
				MapUtil.cacheAwareMap(new ConcurrentHashMap<String, List<Code>>());

	/**
	 * コンストラクタ
	 */
	private CodeKeeperImpl() {
		super(24);
		if (INSTANCE.get() != null) {
			throw new AssertionError();
		}
		super.startTimer();
	}

	/**
	 * インスタンス取得
	 *
	 * @return インスタンス
	 */
	public static CodeKeeperImpl getInstance() {
		if (INSTANCE.get() == null) {
			INSTANCE.compareAndSet(null, new CodeKeeperImpl());
		}
		return INSTANCE.get();
	}

	/**
	 * 削除処理
	 *
	 * @param kbn コード種別
	 */
	@Override
	public void remove(final String kbn) {
		this.code.remove(kbn);
	}

	/**
	 * 選択項目取得
	 *
	 * @param kbn コード種別
	 * @param cd コード
	 * @return コードマスタ
	 */
	@Override
	public Code getCode(final String kbn, final String cd) {
		List<Code> l = getCodeList(kbn);
		if (l != null) {
			for (final Code c : l) {
				if (c.getCode().equals(cd)) {
					return c;
				}
			}
		}
		return null;
	}

	/**
	 * マスタ情報の存在と有効を確認
	 *
	 * @param kbn コード種別
	 * @param cd コード
	 * @return 存在し有効な場合 true
	 */
	@Override
	public boolean isValidCode(final String kbn, final String cd) {
		return getCode(kbn, cd) != null;
	}

	/**
	 * コードリスト取得
	 *
	 * @param kbn コード種別
	 * @return コードリスト
	 */
	private List<Code> setCodeList(final String kbn) {
		try (Jdbc conn = JdbcSource.getConnection()) {
			try (PreparedStatement psmt = conn.readonlyStatement(SEL_MS_CODE)) {
				psmt.setString(1, kbn);

				List<Code> l = null;
				try (ResultSet rs = psmt.executeQuery()) {
					while (rs.next()) {
						Code cd = new Code(
								rs.getString(1), rs.getString(2),
								rs.getString(3), rs.getString(4),
								rs.getString(5), rs.getString(6),
								rs.getString(7));
						if (l == null) {
							l = new ArrayList<>();
						}
						l.add(cd);
					}
				}
				return l;
			}
		} catch (SQLException ex) {
			ThrowableUtil.error(ex);
			throw new PhysicalException(ex);
		}
	}

	/**
	 * コードマスタリスト取得
	 *
	 * @param kbn コード種別
	 * @return コードマスタリスト
	 */
	@Override
	public List<Code> getCodeList(final String kbn) {
		List<Code> l = this.code.get(kbn);
		if (l == null) {
			l = setCodeList(kbn);
			if (l != null) {
				this.code.putIfAbsent(kbn, Collections.unmodifiableList(l));
			} else {
				this.code.putIfAbsent(kbn, Collections.EMPTY_LIST);
			}
		} else if (l.isEmpty()) {
			l = null;
		}
		return l;
	}

	/**
	 * 初期処理を行う。
	 *
	 */
	@Override
	public void init() {
		this.code.clear();
	}
}
